//------------------------------------------------------------------------------ 
//  <copyright file="SourceReaderCallback.cpp" company="Microsoft Corporation">
// The MIT License (MIT)
// 
// Copyright (c) 2014, Microsoft Corporation
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//  </copyright>
//------------------------------------------------------------------------------
#include "..\stdafx.h"
using namespace System::Runtime::InteropServices;

#include "SourceReaderCallback.h"
#include "ks.h"
#include "ksmedia.h"

namespace Microsoft {
    namespace Robotics {
        namespace Vision {
            namespace Cameras {
				namespace Webcam {

/// <summary>
// Creates an instance of the capture device which implements <c> IMFSourceReaderCallback </c>
/// </summary>
///<param name="ppPlayer">The reference to the source reader to the created</param>
///<returns> S_OK if succeeded. Error code if not.</returns>
HRESULT SourceReaderCallback::CreateInstance(SourceReaderCallback **ppCapture)
{
    if (ppCapture == NULL)
    {
        return E_POINTER;
    }

    SourceReaderCallback *pCapture = new SourceReaderCallback();

    if (pCapture == NULL)
    {
        return E_OUTOFMEMORY;
    }

    // The SourceReaderCallback constructor sets the ref count to 1.
    *ppCapture = pCapture;

    return S_OK;
}

/// <summary>
/// Constructor is private. Use static CreateInstance method to instantiate.
/// </summary>   
SourceReaderCallback::SourceReaderCallback() 
: m_pReader(NULL)
, m_lRefCount(1)
, m_readSampleHandler(NULL)
{
}

/// <summary>
// Destructor is private. Caller should call Release.
/// </summary>
SourceReaderCallback::~SourceReaderCallback()
{
}

///<remarks>IUnknown methods</remarks>

/// <summary>
// Increments the reference count for an interface on an object
/// </summary>
///<returns> The method returns the new reference count.</returns>
ULONG SourceReaderCallback::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}


/// <summary>
// Decrements the reference count for an interface on an object
/// </summary>
///<returns> The method returns the new reference count.</returns>
ULONG SourceReaderCallback::Release()
{
    ULONG ulCount = InterlockedDecrement(&m_lRefCount);
    if (ulCount == 0)
    {
        delete this;
    }
    return ulCount;
}

/// <summary>
// Retrieves pointers to the supported interfaces on an object
/// </summary>
///<param name="ppv">The reference to interface to be retrieved</param>
///<returns> S_OK if succeeded. Error code if not.</returns>
HRESULT SourceReaderCallback::QueryInterface(REFIID riid, void** ppv)
{
    if (riid == __uuidof(IMFSourceReaderCallback) || riid == __uuidof(IUnknown))
    {
        *ppv = this;
        AddRef();
        return S_OK;
    }

    return E_NOINTERFACE;
}

/// <summary>
// Called when the IMFSourceReader::ReadSample method completes
/// </summary>
///<param name="hrStatus">
/// The status code. 
/// If an error occurred while processing the next smple, this parameter contains the error code 
/// </param>
///<param name="dwStreamIndex">The zero-based index of the stream that delivered the sample</param>
///<param name="dwStreamFlags">A bitwise OR of zero or more flags from the MF_SOURCE_READER_FLAG enumeration</param>
///<param name="llTimestamp">
/// The time stamp of the sample, or the time of the stream event indicated in dwStreamFlags. 
/// The time is given in 100-nanosecond units
/// </param>
///<param name="pSample">A pointer to the IMFSample interface of a media sample. This parameter might be NULL.</param>
///<returns> S_OK if succeeded. Error code if not.</returns>
HRESULT SourceReaderCallback::OnReadSample(
    _In_ HRESULT hrStatus,
    _In_ DWORD,  // dwStreamIndex
    _In_ DWORD,  // dwStreamFlags
    _In_ LONGLONG llTimeStamp,
    _In_opt_ IMFSample *pSample)      // Can be NULL
{
    HRESULT hr = S_FALSE;
    DWORD count = 0;
    DWORD cbLength = 0;
    IMFMediaBuffer *pBuffer = NULL;

    __try{

        if (FAILED(hrStatus))
        {
            hr = hrStatus;
            MF_CHKHR(hr);
        }

        if (pSample)
        {
            // If we have a sample handler, setup the call
            if (m_readSampleHandler != NULL)
            {
                hr = pSample->GetBufferCount(&count);
                MF_CHKHR(hr);

                if (count > 1)
                {
                    hr = E_INVALIDARG;
                    MF_CHKHR(hr);
                }

                hr = pSample->GetBufferByIndex(0, &pBuffer);
                MF_CHKHR(hr);

                hr = pBuffer->GetCurrentLength(&cbLength);
                MF_CHKHR(hr);

                if (cbLength > 0)
                {
                    BYTE *pbData = NULL;

                    hr = pBuffer->Lock(&pbData, NULL, NULL);
                    MF_CHKHR(hr);
                    BYTE* pbYUY2 = NULL;
                    int cbYUY2Length = cbLength;                               
                    
                    pbYUY2 = new BYTE[cbYUY2Length]; 
                    memcpy_s(pbYUY2, cbYUY2Length, pbData, cbLength);

                    (*m_readSampleHandler)(pbYUY2, cbYUY2Length);
                    SAFE_DELETE_ARRAY(pbYUY2);

                    pBuffer->Unlock();
                }
            }
        }
    }
    __finally
    {        
        MF_RELEASE(pBuffer);
        //The first time you call ReadSample, we likely won't get a sample, so setup again       
        // Read another sample.
        m_pReader->ReadSample(
            (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
            0,
            NULL,   // actual
            NULL,   // flags
            NULL,   // timestamp
            NULL);  // sample
    }
    return hr;
}

/// <summary>
///  Sets up the capture pipeline if required and sets up async sample capture
/// </summary>        
///<param name="handler">Handler to call after completing read sample</param>
void SourceReaderCallback::CaptureSample(ReadSampleHandlerForDevice handler)
{
    m_readSampleHandler = handler;

    // We have to tickle the reader
    m_pReader->ReadSample(
        (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
        0,
        NULL,
        NULL,
        NULL,
        NULL);
}
}}}}}
